﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using YArchitech.Revit;
using YJKArchUtils.Utils;
using YJKRevitBase.TopologySuite;
using YJKRevitKernel.RevitTopologySuite;
using YJKGeometryFuncs;

namespace YJKRevitDimensionText.OneKeyPlanDim.Utility
{
	public class GeometryHelper
	{
		public static RevitOperationDataBean GetOneIntersection(List<Curve> lstCv1, List<Curve> lstCv2, BooleanOperatorKindEnum booleanOperatorKindEnum)
		{
			RevitOperationDataBean result = null;
			try
			{
				RevitPolygonDataBean revitPolygonDataBean = new RevitPolygonDataBean(lstCv1, null, 3, 4u);
				RevitPolygonDataBean revitPolygonDataBean2 = new RevitPolygonDataBean(lstCv2, null, 3, 4u);
				List<RevitOperationDataBean> list = new List<RevitOperationDataBean>();
                if (new RevitPolygonBooleanOperater().TryGetBooleanResult(revitPolygonDataBean, revitPolygonDataBean2, 0, out list))
				{
					double num = 0.0;
					foreach (RevitOperationDataBean revitOperationDataBean in list)
					{
						RevitPolygonDataBean revitPolygonDataBean3 = revitOperationDataBean as RevitPolygonDataBean;
                        if (revitPolygonDataBean3 != null && revitPolygonDataBean3.Area != null && YJKArch.Utils.Geometry.LessThan(num, revitPolygonDataBean3.Area.Value))
						{
							num = revitPolygonDataBean3.Area.Value;
							result = revitPolygonDataBean3;
						}
					}
                    if (YJKArch.Utils.Geometry.LessThan(num, 1.0))
					{
						return null;
					}
				}
			}
			catch
			{
			}
			return result;
		}

		public static RevitOperationDataBean GetOneIntersection(CurveLoop lstCv1, List<Curve> lstCv2, BooleanOperatorKindEnum booleanOperatorKindEnum)
		{
			RevitOperationDataBean result = null;
			try
			{
				RevitPolygonDataBean revitPolygonDataBean = new RevitPolygonDataBean(lstCv1, null, 3);
				RevitPolygonDataBean revitPolygonDataBean2 = new RevitPolygonDataBean(lstCv2, null, 3, 4u);
				List<RevitOperationDataBean> list = new List<RevitOperationDataBean>();
                if (new RevitPolygonBooleanOperater().TryGetBooleanResult(revitPolygonDataBean, revitPolygonDataBean2, 0, out list))
				{
					double num = 0.0;
					foreach (RevitOperationDataBean revitOperationDataBean in list)
					{
						RevitPolygonDataBean revitPolygonDataBean3 = revitOperationDataBean as RevitPolygonDataBean;
                        if (revitPolygonDataBean3 != null && revitPolygonDataBean3.Area != null && YJKArch.Utils.Geometry.LessThan(num, revitPolygonDataBean3.Area.Value))
						{
							num = revitPolygonDataBean3.Area.Value;
							result = revitPolygonDataBean3;
						}
					}
                    if (YJKArch.Utils.Geometry.LessThan(num, 1.0))
					{
						return null;
					}
				}
			}
			catch
			{
			}
			return result;
		}

		public static List<RevitOperationDataBean> GetIntersection(List<CurveLoop> lstCvLoop, List<Curve> lstCv2, BooleanOperatorKindEnum booleanOperatorKindEnum)
		{
			List<RevitOperationDataBean> list = new List<RevitOperationDataBean>();
			foreach (CurveLoop lstCv3 in lstCvLoop)
			{
				RevitOperationDataBean oneIntersection = GeometryHelper.GetOneIntersection(lstCv3, lstCv2, booleanOperatorKindEnum);
				if (oneIntersection != null)
				{
					list.Add(oneIntersection);
				}
			}
			return list;
		}

		public static List<RevitOperationDataBean> GetIntersection(List<Curve> lstCvLoop, List<Curve> lstCv2, BooleanOperatorKindEnum booleanOperatorKindEnum)
		{
			List<RevitOperationDataBean> list = new List<RevitOperationDataBean>();
			RevitOperationDataBean oneIntersection = GeometryHelper.GetOneIntersection(lstCvLoop, lstCv2, booleanOperatorKindEnum);
			if (oneIntersection != null)
			{
				list.Add(oneIntersection);
			}
			return list;
		}

		public static List<Curve> GetAllEdgeList(Face planarFace)
		{
			List<Curve> list = new List<Curve>();
			foreach (CurveLoop curveLoop in planarFace.GetEdgesAsCurveLoops())
			{
				foreach (Curve item in curveLoop)
				{
					list.Add(item);
				}
			}
			return list;
		}

		public static bool OutWallDir(List<CurveLoop> lstCvloop, Wall wall, out XYZ ptOutWallDir)
		{
			ptOutWallDir = XYZ.BasisY;
			if (wall == null || lstCvloop.Count == 0)
			{
				return false;
			}
			XYZ xyz = XYZ.Zero;
			XYZ xyz2 = XYZ.BasisX;
			try
			{
				Curve xyplaneCurve = CommonInterface.GetXYPlaneCurve((wall.Location as LocationCurve).Curve);
				if (xyplaneCurve is Line)
				{
					Line line = xyplaneCurve as Line;
                    xyz = YJKArch.Utils.Geometry.CalculatMidPoint(line);
					xyz2 = line.Direction;
				}
				else if (xyplaneCurve is Arc)
				{
					Arc arc = xyplaneCurve as Arc;
                    xyz = YJKArch.Utils.Geometry.CalculatMidPoint(arc);
					xyz2 = GeometryHelper.GetTangentAtParameter(arc, 0.5);
				}
				ptOutWallDir = xyz2.CrossProduct(XYZ.BasisZ);
				XYZ xyz3 = xyz + ptOutWallDir * Common.MMToFeet(500.0);
				bool flag = true;
				foreach (CurveLoop curveLoop in lstCvloop)
				{
					List<XYZ> list = new List<XYZ>();
					foreach (Curve curve in curveLoop)
					{
						list.AddRange(curve.Tessellate());
					}
                    if (YJKArch.Utils.Geometry.PointInPloygon(list, xyz3, 0.0001, 0.0001) == 1)
					{
						flag = false;
						break;
					}
				}
				if (!flag)
				{
					ptOutWallDir = -ptOutWallDir;
				}
			}
			catch
			{
				return false;
			}
			return true;
		}

		private static XYZ GetTangentAtParameter(Curve curve, double dParameter)
		{
			return curve.ComputeDerivatives(dParameter, true).BasisX.Normalize();
		}

		public static List<Curve> GetAllEdgeList(Element ThisElement)
		{
			List<Curve> list = new List<Curve>();
			foreach (Solid solid in GeometryHelper.GetLstSolid(ThisElement))
			{
				foreach (object obj in solid.Edges)
				{
					Edge edge = (Edge)obj;
					list.Add(edge.AsCurve());
				}
			}
			return list;
		}

		public static List<PlanarFace> GetAllPlanarFace(Element ThisElement)
		{
			List<PlanarFace> list = new List<PlanarFace>();
			foreach (Solid solid in GeometryHelper.GetLstSolid(ThisElement))
			{
				list.AddRange(GeometryHelper.GetSolidPlanarFace(solid));
			}
			return list;
		}

		public static List<RuledFace> GetAllRuledFace(Element ThisElement)
		{
			List<RuledFace> list = new List<RuledFace>();
			foreach (Solid solid in GeometryHelper.GetLstSolid(ThisElement))
			{
				list.AddRange(GeometryHelper.GetSolidRuledFace(solid));
			}
			return list;
		}

		public static List<PlanarFace> GetBasePlanarFace(List<PlanarFace> lstPlanarFace)
		{
			List<PlanarFace> list = new List<PlanarFace>();
			try
			{
				foreach (PlanarFace planarFace in lstPlanarFace)
				{
					if (GeometryHelper.VectorParallelVector(XYZ.BasisZ, planarFace.FaceNormal))
					{
						list.Add(planarFace);
					}
				}
			}
			catch
			{
			}
			return list;
		}

		public static List<PlanarFace> PlanarFaceWithCommonLine(List<PlanarFace> lstPlanarFace, Line lineTarget)
		{
			List<PlanarFace> list = new List<PlanarFace>();
			foreach (PlanarFace planarFace in lstPlanarFace)
			{
				List<Curve> allEdgeList = GeometryHelper.GetAllEdgeList(planarFace);
				bool flag = false;
				foreach (Curve curve in allEdgeList)
				{
					Line line = curve as Line;
					if (!(line == null) && GeometryHelper.IsCommonLine(line, lineTarget))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					list.Add(planarFace);
				}
			}
			return list;
		}

		public static List<Solid> GetLstSolid(Element ThisElement)
		{
			List<Solid> result = new List<Solid>();
			Options options = new Options();
			GeometryHelper.GetLstSolid(ThisElement.get_Geometry(options), ref result, null);
			return result;
		}

		public static void GetLstSolid(GeometryElement useGermetry, ref List<Solid> lstSolid, Transform useTransform = null)
		{
			foreach (GeometryObject geometryObject in useGermetry)
			{
				if (null != geometryObject as Solid)
				{
					Solid solid = geometryObject as Solid;
					if (useTransform != null)
					{
						solid = SolidUtils.CreateTransformed(solid, useTransform);
					}
					lstSolid.Add(solid);
				}
				else if (null != geometryObject as GeometryInstance)
				{
					GeometryInstance geometryInstance = geometryObject as GeometryInstance;
					Transform transform = geometryInstance.Transform;
					if (useTransform != null)
					{
						transform = useTransform.Multiply(transform);
					}
					GeometryHelper.GetLstSolid(geometryInstance.SymbolGeometry, ref lstSolid, transform);
				}
			}
		}

		public static List<PlanarFace> GetSolidPlanarFace(Solid solid)
		{
			List<PlanarFace> list = new List<PlanarFace>();
			foreach (object obj in solid.Faces)
			{
				Face face = (Face)obj;
				if (face is PlanarFace)
				{
					list.Add(face as PlanarFace);
				}
			}
			return list;
		}

		public static List<RuledFace> GetSolidRuledFace(Solid solid)
		{
			List<RuledFace> list = new List<RuledFace>();
			foreach (object obj in solid.Faces)
			{
				Face face = (Face)obj;
				if (face is RuledFace)
				{
					list.Add(face as RuledFace);
				}
			}
			return list;
		}

		public static bool Is2PlanarFaceHasCommonEdge(PlanarFace planarFace1, PlanarFace planarFace2, out List<Curve> lstCurve)
		{
			lstCurve = new List<Curve>();
			if (planarFace2 == null || planarFace1 == null)
			{
				return false;
			}
			List<Curve> list = new List<Curve>();
			List<Curve> list2 = new List<Curve>();
			list.AddRange(GeometryHelper.LstCvLoop2LstCurve(planarFace1.GetEdgesAsCurveLoops().ToList<CurveLoop>()));
			list2.AddRange(GeometryHelper.LstCvLoop2LstCurve(planarFace2.GetEdgesAsCurveLoops().ToList<CurveLoop>()));
			foreach (Curve curve in list)
			{
				XYZ hyendPoint = RevitDocumentExtension.GetEndPoint(curve, 0);
				XYZ hyendPoint2 = RevitDocumentExtension.GetEndPoint(curve, 1);
				foreach (Curve curve2 in list2)
				{
					XYZ hyendPoint3 = RevitDocumentExtension.GetEndPoint(curve2, 0);
					XYZ hyendPoint4 = RevitDocumentExtension.GetEndPoint(curve2, 1);
					if ((hyendPoint.IsAlmostEqualTo(hyendPoint3, 0.0001) && hyendPoint2.IsAlmostEqualTo(hyendPoint4, 0.0001)) || (hyendPoint.IsAlmostEqualTo(hyendPoint4, 0.0001) && hyendPoint2.IsAlmostEqualTo(hyendPoint3, 0.0001)))
					{
						lstCurve.Add(curve);
					}
				}
			}
			return lstCurve.Count > 0;
		}

		public static bool IsCommonLine(Line line1, Line line2)
		{
			if (line1 == null || line2 == null || !line1.IsBound || !line2.IsBound)
			{
				return false;
			}
			XYZ hyendPoint = RevitDocumentExtension.GetEndPoint(line1, 0);
			XYZ hyendPoint2 = RevitDocumentExtension.GetEndPoint(line1, 1);
			XYZ hyendPoint3 = RevitDocumentExtension.GetEndPoint(line2, 0);
			XYZ hyendPoint4 = RevitDocumentExtension.GetEndPoint(line2, 1);
			return (hyendPoint.IsAlmostEqualTo(hyendPoint3, 0.0001) && hyendPoint2.IsAlmostEqualTo(hyendPoint4, 0.0001)) || (hyendPoint.IsAlmostEqualTo(hyendPoint4, 0.0001) && hyendPoint2.IsAlmostEqualTo(hyendPoint3, 0.0001));
		}

		public static bool IsInCommonLine(Line line1, Line line2)
		{
            return !(line1 == null) && !(line2 == null) && GeometryHelper.VectorParallelVector(line1.Direction, line2.Direction) && YJKArch.Utils.Geometry.IsEqual(GeometryHelper.Distance2ParallelLine(line1, line2), 0.0, 1E-06);
		}

		public static List<Curve> LstCvLoop2LstCurve(List<CurveLoop> lstCurveLoop)
		{
			List<Curve> list = new List<Curve>();
			foreach (CurveLoop source in lstCurveLoop)
			{
				list.AddRange(source.Cast<Curve>().ToList<Curve>());
			}
			return list;
		}

		public static List<Line> GetParallelLineInLstCv(Line line, List<Curve> lstCv)
		{
			List<Line> list = new List<Line>();
			if (line == null || lstCv == null || lstCv.Count == 0)
			{
				return list;
			}
			foreach (Curve curve in lstCv)
			{
				Line line2 = curve as Line;
                if (GeometryHelper.VectorParallelVector(line.Direction, line2.Direction) && !YJKArch.Utils.Geometry.IsEqual(GeometryHelper.Distance2ParallelLine(line, line2), 0.0, 1E-06))
				{
					list.Add(line2);
				}
			}
			return list;
		}

		public static double Distance2ParallelLine(Line line1, Line line2)
		{
			double result = 0.0;
			if (line1 == null || line2 == null)
			{
				return result;
			}
			Line line3 = line1.Clone() as Line;
			if (line3.IsBound)
			{
				line3.MakeUnbound();
			}
			return line3.Distance(line2.Origin);
		}

		public static bool VectorVerticalVector(XYZ vec, XYZ vecTarget)
		{
			vec = vec.Normalize();
			vecTarget = vecTarget.Normalize();
			return Math.Abs(vec.DotProduct(vecTarget)) < 1E-07;
		}

		public static bool VectorParallelVector(XYZ vec, XYZ vecTarget)
		{
			if (vec == null || vecTarget == null)
			{
				return false;
			}
			vec = vec.Normalize();
			vecTarget = vecTarget.Normalize();
			return vec.IsAlmostEqualTo(-1.0 * vecTarget, 0.001) || vec.IsAlmostEqualTo(vecTarget, 0.001);
		}

		public static bool PointOnFloor(XYZ ptOld, List<Curve> lstCv)
		{
			if (ptOld == null || lstCv == null)
			{
				return false;
			}
			XYZ xyz = new XYZ(ptOld.X, ptOld.Y, 0.0);
			List<XYZ> list = new List<XYZ>();
			foreach (Curve curve in lstCv)
			{
				list.AddRange(curve.Tessellate());
			}
            return YJKArch.Utils.Geometry.PointInPloygon(list, xyz, 0.0001, 0.0001) == 1;
		}

		public static bool IsInCvLoop(XYZ pt, List<Curve> lstCv)
		{
			List<XYZ> list = new List<XYZ>();
			foreach (Curve curve in lstCv)
			{
				list.AddRange(curve.Tessellate());
			}
            return YJKArch.Utils.Geometry.PointInPloygon(list, pt, 0.0001, 0.0001) == 1;
		}

		public static bool IsInCvLoop(XYZ pt, List<CurveLoop> lstCvLoop)
		{
			foreach (CurveLoop curveLoop in lstCvLoop)
			{
				List<XYZ> list = new List<XYZ>();
				foreach (Curve curve in curveLoop)
				{
					list.AddRange(curve.Tessellate());
				}
                if (YJKArch.Utils.Geometry.PointInPloygon(list, pt, 0.0001, 0.0001) == 1)
				{
					return true;
				}
			}
			return false;
		}

		public static bool PointOnFloor(XYZ ptOld, List<XYZ> lstXYZ)
		{
			if (ptOld == null || lstXYZ == null)
			{
				return false;
			}
			XYZ xyz = new XYZ(ptOld.X, ptOld.Y, 0.0);
            return YJKArch.Utils.Geometry.PointInPloygon(lstXYZ, xyz, 0.0001, 0.0001) == 1;
		}

		public static bool Is_Point_OnSegment(Curve cv, XYZ pt)
		{
			try
			{
				XYZ xyzpoint = cv.Project(pt).XYZPoint;
				if (pt.IsAlmostEqualTo(xyzpoint))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		public static List<XYZ> LineWithLstCvIntersect(Line lineTmp, List<Curve> lstCv)
		{
			if (lineTmp.IsBound)
			{
				lineTmp.MakeUnbound();
			}
			List<Curve> list = new List<Curve>();
			list.AddRange(CommonInterface.GetXYPlaneCurve(lstCv));
			Curve xyplaneCurve = CommonInterface.GetXYPlaneCurve(lineTmp);
			List<XYZ> list2 = new List<XYZ>();
			foreach (Curve curve in list)
			{
				IntersectionResultArray intersectionResultArray;
                if ((int)xyplaneCurve.Intersect(curve, out intersectionResultArray) == 8)
				{
					foreach (object obj in intersectionResultArray)
					{
						IntersectionResult intersectionResult = (IntersectionResult)obj;
						list2.Add(intersectionResult.XYZPoint);
					}
				}
			}
			return list2;
		}

		public static XYZ GeometricCenterOfPlanarFace(PlanarFace planarFace)
		{
			CurveArray curveArray = new CurveArray();
			foreach (CurveLoop curveLoop in planarFace.GetEdgesAsCurveLoops())
			{
				foreach (Curve curve in curveLoop)
				{
					curveArray.Append(curve);
				}
			}
			XYZ result = XYZ.Zero;
			try
			{
				result = YJKRevitTools.Centroid(curveArray);
			}
			catch
			{
			}
			return result;
		}

		public static XYZ GeometricCenterByLstCvPoint(List<Curve> lstCv)
		{
			double minValue = double.MinValue;
			double maxValue = double.MaxValue;
			double maxValue2 = double.MaxValue;
			double minValue2 = double.MinValue;
			double num = 0.0;
			foreach (Curve curve in lstCv)
			{
				XYZ hyendPoint = RevitDocumentExtension.GetEndPoint(curve, 0);
				XYZ hyendPoint2 = RevitDocumentExtension.GetEndPoint(curve, 1);
				GeometryHelper.CmpDoubleValue(ref minValue, ref maxValue, ref minValue2, ref maxValue2, hyendPoint);
				GeometryHelper.CmpDoubleValue(ref minValue, ref maxValue, ref minValue2, ref maxValue2, hyendPoint2);
				num = hyendPoint.Z;
			}
			return new XYZ((minValue + maxValue) / 2.0, (minValue2 + maxValue2) / 2.0, num);
		}

		private static void CmpDoubleValue(ref double dMaxX, ref double dMinX, ref double dMaxY, ref double dMinY, XYZ point)
		{
			if (point.X > dMaxX)
			{
				dMaxX = point.X;
			}
			if (point.X < dMinX)
			{
				dMinX = point.X;
			}
			if (point.Y > dMaxY)
			{
				dMaxY = point.Y;
			}
			if (point.Y < dMinY)
			{
				dMinY = point.Y;
			}
		}

		public static XYZ GeometricCenterByLstCv(List<Curve> lstCv)
		{
			CurveArray curveArray = new CurveArray();
			foreach (Curve curve in lstCv)
			{
				curveArray.Append(curve);
			}
			XYZ result = XYZ.Zero;
			try
			{
				result = YJKRevitTools.Centroid(curveArray);
			}
			catch
			{
			}
			return result;
		}

		public static PlanarFace GetMaxAreaFaceFromFloor(Floor floor)
		{
			List<PlanarFace> allPlanarFace = GeometryHelper.GetAllPlanarFace(floor);
			double num = double.MinValue;
			PlanarFace result = null;
			foreach (PlanarFace planarFace in allPlanarFace)
			{
                if (YJKArch.Utils.Geometry.LessThan(num, planarFace.Area))
				{
					num = planarFace.Area;
					result = planarFace;
				}
			}
			return result;
		}

		public static bool TryCutting2Curve(Curve cv1, Curve cv2, out Curve cvPro1, out Curve cvPro2)
		{
			cvPro1 = cv1;
			cvPro2 = cv2;
			try
			{
				Curve curve = cv1.Clone();
				Curve curve2 = cv2.Clone();
				if (curve.IsBound)
				{
					curve.MakeUnbound();
				}
				if (curve2.IsBound)
				{
					curve2.MakeUnbound();
				}
				IntersectionResultArray intersectionResultArray;
                if ((int)curve.Intersect(curve2, out intersectionResultArray) != 8 || intersectionResultArray == null || intersectionResultArray.IsEmpty)
				{
					return false;
				}
				XYZ xyzpoint;
				if (intersectionResultArray.Size == 1)
				{
					xyzpoint = intersectionResultArray.get_Item(0).XYZPoint;
				}
				else
				{
					if (intersectionResultArray.Size != 2)
					{
						return false;
					}
					XYZ xyz = (cv1.GetEndPoint(0) + cv1.GetEndPoint(1)) * 0.5;
					XYZ xyzpoint2 = cv1.Project(xyz).XYZPoint;
                    if (YJKArch.Utils.Geometry.LessThan(xyzpoint2.DistanceTo(intersectionResultArray.get_Item(0).XYZPoint), xyzpoint2.DistanceTo(intersectionResultArray.get_Item(1).XYZPoint)))
					{
						xyzpoint = intersectionResultArray.get_Item(0).XYZPoint;
					}
					else
					{
                        xyzpoint = intersectionResultArray.get_Item(1).XYZPoint;
					}
				}
				Curve curve3 = null;
				if (GeometryHelper.TryCreateNewCurve(cv1, xyzpoint, out curve3))
				{
					cvPro1 = curve3;
				}
				if (GeometryHelper.TryCreateNewCurve(cv2, xyzpoint, out curve3))
				{
					cvPro2 = curve3;
				}
			}
			catch
			{
				return false;
			}
			return true;
		}

		public static bool TryCreateNewCurve(Curve cvOld, XYZ ptNew, out Curve cvNew)
		{
			cvNew = null;
			try
			{
				XYZ endPoint = cvOld.GetEndPoint(0);
				XYZ endPoint2 = cvOld.GetEndPoint(1);
				XYZ xyz = endPoint;
                if (YJKArch.Utils.Geometry.LessThan(endPoint.DistanceTo(ptNew), endPoint2.DistanceTo(ptNew)))
				{
					xyz = endPoint2;
				}
				if (cvOld is Line)
				{
					cvNew = Line.CreateBound(xyz, ptNew);
					return true;
				}
				if (cvOld is Arc)
				{
					XYZ xyz2 = (endPoint + endPoint2) * 0.5;
					XYZ xyzpoint = cvOld.Project(xyz2).XYZPoint;
					cvNew = Arc.Create(xyz, ptNew, xyzpoint);
					return true;
				}
			}
			catch
			{
			}
			return false;
		}
	}
}
